package com.xnyzc.lhy.common.component.amap;

import org.apache.http.HeaderElement;
import org.apache.http.HeaderElementIterator;
import org.apache.http.HttpResponse;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.conn.ConnectionKeepAliveStrategy;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.DefaultHttpRequestRetryHandler;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicHeaderElementIterator;
import org.apache.http.protocol.HTTP;
import org.apache.http.protocol.HttpContext;

import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.TimeUnit;

/**
 * Http Client连接池
 * 部分实现参考 http://hc.apache.org/httpcomponents-client-ga/tutorial/html/connmgmt.html
 * <p>
 * 环境要求： Java 6 及以上
 * <p>
 * 使用方法：
 * <p>
 * 1. Java 8 使用try-with-resources特性
 * <code>
 * <p>
 * HttpClient client = HttpClientPool.getClient();
 * try (CloseableHttpResponse response = client.execute(...)) {
 * <p>
 * // 处理响应内容....
 * <p>
 * } catch (...) {
 * <p>
 * // 处理异常...
 * <p>
 * }
 *
 * </code>
 * <p>
 * 2. Java 8 以下
 * <code>
 * HttpClient client = HttpClientPool.getClient();
 * HttpResponse response = null;
 * try {
 * <p>
 * response = client.execute(...);
 * <p>
 * // 处理响应内容....
 * <p>
 * } catch (...) {
 * <p>
 * // 处理异常
 * <p>
 * } finally {
 * // 释放资源
 * if (response != null) {
 * EntityUtils.consume(response.getEntity());
 * }
 * }
 *
 * </code>
 *hu
 * @author francis
 */

public class HttpClientPool {

    /**
     * 默认连接超时时间 毫秒
     */
    private static final int CONNECTION_TIMEOUT = 2000;

    /**
     * 默认响应超时时间 毫秒
     */
    private static final int RESPONSE_TIMEOUT = 2000;

    /**
     * 连接池最大连接数
     */
    private static final int TOTAL_CONNECTION_NUM = 500;

    /**
     * 单个路由分配的最大连接数
     * 一般设置为单个路由最大并发数
     */
    private static final int ROUTE_MAX_CONNECTION_NUM = 50;

    /**
     * keep live 保持的时间 秒
     */
    private static final int KEEP_LIVE_SECONDS = 30;

    /**
     * 验证keep live连接是否有效的时间间隔 毫秒
     */
    private static final int VALIDATE_CONNECTION_INACTIVITY_INTERVAL = 500;

    /**
     * 连接最大空闲时间 秒
     * 超过此时间 将会被连接池收回
     */
    private static final int MAX_IDLE_CONNECTION_SECONDS = 30;

    /**
     * 主动回收空闲连接的间隔时间 毫秒
     */
    private static final int CONNECTION_RECYCLE_INTERVAL = 5 * 1000;


    private HttpClientPool() {
    }

    private static final PoolingHttpClientConnectionManager CONNECTION_MANAGER;

    private static final ConnectionKeepAliveStrategy CONNECTION_KEEP_ALIVE_STRATEGY;

    static {
        CONNECTION_MANAGER = new PoolingHttpClientConnectionManager();
        // 最大连接数
        CONNECTION_MANAGER.setMaxTotal(TOTAL_CONNECTION_NUM);
        // 每个路由分配的连接池大小
        CONNECTION_MANAGER.setDefaultMaxPerRoute(ROUTE_MAX_CONNECTION_NUM);
        // 在此时间间隔内 不验证连接有效性直接重用连接
        CONNECTION_MANAGER.setValidateAfterInactivity(VALIDATE_CONNECTION_INACTIVITY_INTERVAL);

        // 定时回收
        new Timer("HttpClientReclaimer", true).schedule(new TimerTask() {
            @Override
            public void run() {
                try {
                    synchronized (this) {
                        CONNECTION_MANAGER.closeExpiredConnections();
                        CONNECTION_MANAGER.closeIdleConnections(MAX_IDLE_CONNECTION_SECONDS, TimeUnit.SECONDS);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }, CONNECTION_RECYCLE_INTERVAL, CONNECTION_RECYCLE_INTERVAL);

        // keep live 策略
        CONNECTION_KEEP_ALIVE_STRATEGY = new ConnectionKeepAliveStrategy() {
            @Override
            public long getKeepAliveDuration(HttpResponse response, HttpContext context) {
                HeaderElementIterator it = new BasicHeaderElementIterator(response.headerIterator(HTTP.CONN_KEEP_ALIVE));
                while (it.hasNext()) {
                    HeaderElement he = it.nextElement();
                    String param = he.getName();
                    String value = he.getValue();
                    if (value != null && param.equalsIgnoreCase("timeout")) {
                        return Long.parseLong(value) * 1000;
                    }
                }
                return KEEP_LIVE_SECONDS * 1000;
            }
        };
    }

    /**
     * 获取一个httpClient
     */
    public static CloseableHttpClient getClient() {
        return HttpClients.custom()
                // 连接管理器
                .setConnectionManager(CONNECTION_MANAGER)
                // keep live 策略
                .setKeepAliveStrategy(CONNECTION_KEEP_ALIVE_STRATEGY)
                // 默认请求参数 目前只设置了超时时间
                .setDefaultRequestConfig(RequestConfig.custom()
                        .setSocketTimeout(RESPONSE_TIMEOUT)
                        .setConnectTimeout(CONNECTION_TIMEOUT)
                        .build()
                )
                // 重试策略 不重试
                .setRetryHandler(new DefaultHttpRequestRetryHandler(0, false))
                .build();
    }
}
